#include "Avalon/SerializationTests/DataShuttleTester.hpp"
#include "Avalon/IO/Buffer.hpp"
#include "Avalon/Serialization/DataShuttleFactory.hpp"
#include "Avalon/Serialization/Deserializer.hpp"
#include "Avalon/Serialization/Serializer.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Serialization;
using namespace Avalon::Serialization::Tests;
using namespace boost;
using namespace std;

namespace {
  template<typename T>
  void TestShuttlingReference(DataShuttleFactory* rawFactory, const T& value) {
    scoped_ptr<DataShuttleFactory> factory(rawFactory);
    scoped_ptr<Serializer> serializer(factory->CreateSerializer());
    scoped_ptr<Deserializer> deserializer(factory->CreateDeserializer());
    T inValue = value;
    serializer->Shuttle("value", inValue);
    Buffer buffer = serializer->GetSerializedData();
    deserializer->SetDataSource(buffer.GetData(), buffer.GetSize());
    T outValue;
    deserializer->Shuttle("value", outValue);
    CPPUNIT_ASSERT(inValue == outValue);
  }

  template<typename T>
  void TestShuttlingConstant(DataShuttleFactory* rawFactory, const T& value) {
    scoped_ptr<DataShuttleFactory> factory(rawFactory);
    scoped_ptr<Serializer> serializer(factory->CreateSerializer());
    scoped_ptr<Deserializer> deserializer(factory->CreateDeserializer());
    serializer->Shuttle("value", value);
    Buffer buffer = serializer->GetSerializedData();
    deserializer->SetDataSource(buffer.GetData(), buffer.GetSize());
    T outValue;
    deserializer->Shuttle("value", outValue);
    CPPUNIT_ASSERT(value == outValue);
  }

  struct A {
    virtual void Shuttle(DataShuttle* shuttle, unsigned int version) = 0;
  };

  struct B : A {
    int m_x;

    virtual void Shuttle(DataShuttle* shuttle, unsigned int version) {
      shuttle->Shuttle("x", m_x);
    }
  };

  struct C : A {
    int m_y;

    virtual void Shuttle(DataShuttle* shuttle, unsigned int version) {
      shuttle->Shuttle("y", m_y);
    }
  };
}

AVALON_SERIALIZABLE(B, "B", 0);
AVALON_SERIALIZABLE(C, "C", 0);

void DataShuttleTester::TestShuttlingBool() {
  TestShuttlingReference(CreateDataShuttleFactory(), true);
  TestShuttlingConstant(CreateDataShuttleFactory(), true);
  TestShuttlingReference(CreateDataShuttleFactory(), false);
  TestShuttlingConstant(CreateDataShuttleFactory(), false);
}

void DataShuttleTester::TestShuttlingChar() {
  TestShuttlingReference(CreateDataShuttleFactory(), '\0');
  TestShuttlingConstant(CreateDataShuttleFactory(), '\0');
  TestShuttlingReference(CreateDataShuttleFactory(), 'a');
  TestShuttlingConstant(CreateDataShuttleFactory(), 'a');
  TestShuttlingReference(CreateDataShuttleFactory(), static_cast<char>(-1));
  TestShuttlingConstant(CreateDataShuttleFactory(), static_cast<char>(-1));
}

void DataShuttleTester::TestShuttlingUnsignedChar() {
  TestShuttlingReference(CreateDataShuttleFactory(), '\0');
  TestShuttlingConstant(CreateDataShuttleFactory(), '\0');
  TestShuttlingReference(CreateDataShuttleFactory(), 'a');
  TestShuttlingConstant(CreateDataShuttleFactory(), 'a');
}

void DataShuttleTester::TestShuttlingShort() {
  TestShuttlingReference(CreateDataShuttleFactory(), static_cast<short>(0));
  TestShuttlingConstant(CreateDataShuttleFactory(), static_cast<short>(0));
  TestShuttlingReference(CreateDataShuttleFactory(), static_cast<short>(1));
  TestShuttlingConstant(CreateDataShuttleFactory(), static_cast<short>(1));
  TestShuttlingReference(CreateDataShuttleFactory(), static_cast<short>(-1));
  TestShuttlingConstant(CreateDataShuttleFactory(), static_cast<short>(-1));
}

void DataShuttleTester::TestShuttlingUnsignedShort() {
  TestShuttlingReference(CreateDataShuttleFactory(),
    static_cast<unsigned short>(0));
  TestShuttlingConstant(CreateDataShuttleFactory(),
    static_cast<unsigned short>(0));
  TestShuttlingReference(CreateDataShuttleFactory(),
    static_cast<unsigned short>(1));
  TestShuttlingConstant(CreateDataShuttleFactory(),
    static_cast<unsigned short>(1));
}

void DataShuttleTester::TestShuttlingInt() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0);
  TestShuttlingReference(CreateDataShuttleFactory(), 1);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1);
  TestShuttlingReference(CreateDataShuttleFactory(), -1);
  TestShuttlingConstant(CreateDataShuttleFactory(), -1);
}

void DataShuttleTester::TestShuttlingUnsignedInt() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0U);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0U);
  TestShuttlingReference(CreateDataShuttleFactory(), 1U);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1U);
}

void DataShuttleTester::TestShuttlingLong() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0L);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0L);
  TestShuttlingReference(CreateDataShuttleFactory(), 1L);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1L);
}

void DataShuttleTester::TestShuttlingUnsignedLong() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0UL);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0UL);
  TestShuttlingReference(CreateDataShuttleFactory(), 1UL);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1UL);
}

void DataShuttleTester::TestShuttlingLongLong() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0LL);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0LL);
  TestShuttlingReference(CreateDataShuttleFactory(), 1LL);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1LL);
}

void DataShuttleTester::TestShuttlingUnsignedLongLong() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0ULL);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0ULL);
  TestShuttlingReference(CreateDataShuttleFactory(), 1ULL);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1ULL);
}

void DataShuttleTester::TestShuttlingFloat() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0.0F);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0.0F);
  TestShuttlingReference(CreateDataShuttleFactory(), 1.0F);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1.0F);
}

void DataShuttleTester::TestShuttlingDouble() {
  TestShuttlingReference(CreateDataShuttleFactory(), 0.0);
  TestShuttlingConstant(CreateDataShuttleFactory(), 0.0);
  TestShuttlingReference(CreateDataShuttleFactory(), 1.0);
  TestShuttlingConstant(CreateDataShuttleFactory(), 1.0);
}

void DataShuttleTester::TestShuttlingString() {
  TestShuttlingReference(CreateDataShuttleFactory(), string("hello world"));
  TestShuttlingReference(CreateDataShuttleFactory(), string(""));
}

void DataShuttleTester::TestShuttlingBuffer() {}

void DataShuttleTester::TestShuttlingPolymorphicType() {
  scoped_ptr<DataShuttleFactory> factory(CreateDataShuttleFactory());
  scoped_ptr<Serializer> serializer(factory->CreateSerializer());
  scoped_ptr<Deserializer> deserializer(factory->CreateDeserializer());
  scoped_ptr<B> inB(new B());
  inB->m_x = 31415;
  serializer->Shuttle("valueA", inB);
  Buffer buffer = serializer->GetSerializedData();
  deserializer->SetDataSource(buffer.GetData(), buffer.GetSize());
  scoped_ptr<A> outB;
  deserializer->Shuttle("valueA", outB);
  CPPUNIT_ASSERT(dynamic_cast<B*>(outB.get()) != NULL);
  CPPUNIT_ASSERT(static_cast<B*>(outB.get())->m_x == inB->m_x);
}
